package com.niiwoo.civet.account.service.local.withdraw;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.niiwoo.civet.account.dao.entity.*;
import com.niiwoo.civet.account.dao.mapper.BankCardMapperExt;
import com.niiwoo.civet.account.dao.mapper.BankTypeMapperExt;
import com.niiwoo.civet.account.dao.mapper.WithdrawOrderMapper;
import com.niiwoo.civet.account.dao.mapper.WithdrawOrderMapperExt;
import com.niiwoo.civet.account.dto.request.WithdrawOrderQueryRequest;
import com.niiwoo.civet.account.dto.response.BankInfoRespDTO;
import com.niiwoo.civet.account.dto.response.WithdrawOrderResponseDTO;
import com.niiwoo.civet.account.enums.WithdrawEndTypeEnum;
import com.niiwoo.civet.account.enums.WithdrawOrderAuditStatusEnum;
import com.niiwoo.civet.account.enums.WithdrawOrderStatusEnum;
import com.niiwoo.civet.account.service.local.AccountService;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.lanmao.enums.WithdrawTypeEnum;
import com.niiwoo.tripod.provider.exception.BizException;

import lombok.extern.slf4j.Slf4j;

import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 提现订单表Service
 *
 * @author xiaoyingping
 * @create 2017-12-05 17:31
 **/
@Slf4j
@Service
public class WithdrawOrderService {

    @Autowired
    private WithdrawOrderMapper withdrawOrderMapper;
    @Autowired
    private WithdrawOrderMapperExt withdrawOrderMapperExt;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private AccountService accountService;
    @Autowired
    private BankCardMapperExt bankCardMapperExt;
    @Autowired
    private BankTypeMapperExt bankTypeMapperExt;
    @Autowired
    private PrivacyMasks privacyMasks;


    //Redis key值:当前的提现应垫付总额
    private static String REDIS_KEY_ADVANCE_TOTAL = "withdraw_advance_total";


    /**
     * 初始化提现订单
     *
     * @param withdrawOrder
     * @return
     */
    @Transactional
    public void initWithdrawOrder(WithdrawOrder withdrawOrder) {
        AccountBase accountBase = accountService.loadAccountByIdForUpdate(withdrawOrder.getAccountId());
        //再次判断余额
        if (accountBase.getAvailableAmount().compareTo(withdrawOrder.getAmount()) < 0) {
            throw new BizException("ACC10007");
        }
        withdrawOrderMapperExt.insertSelective(withdrawOrder);
    }

    /**
     * 查询提现订单（不存在时会抛出异常）
     *
     * @param withdrawOrderId
     * @return
     */
    public WithdrawOrder loadWithdrawOrderById(Long withdrawOrderId) {
        WithdrawOrder withdrawOrder = withdrawOrderMapper.selectByPrimaryKey(withdrawOrderId);
        if (Objects.isNull(withdrawOrder)) {
            throw new BizException("ACC10031");
        }

        return withdrawOrder;
    }


    /**
     * 从主库查询提现订单
     * @param withdrawOrderId
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public WithdrawOrder getWithdrawOrderByIdFromMaster(Long withdrawOrderId){
        WithdrawOrder withdrawOrder = withdrawOrderMapper.selectByPrimaryKey(withdrawOrderId);
        return withdrawOrder;
    }

    /**
     * 根据请求流水号查询提现订单
     *
     * @param requestNo
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public WithdrawOrder loadWithdrawOrderByRequestNo(String requestNo) {
        Assert.hasLength(requestNo, "requestNo 不能为空");
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andRequestNoEqualTo(requestNo);

        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(withdrawOrderList)) {
            throw new BizException("ACC10031");
        }
        return withdrawOrderList.get(0);
    }

    /**
     * 更新提现订单的状态
     *
     * @param withdrawOrderId
     * @param statusEnum
     * @param updateUserId
     * @return
     */
    public int updateStatus(Long withdrawOrderId, WithdrawOrderStatusEnum statusEnum, Long updateUserId) {
        Assert.notNull(withdrawOrderId, "withdrawOrderId不能为空");
        Assert.notNull(statusEnum, "statusEnum不能为空");

        //修改状态
        Date now = new Date();
        WithdrawOrder record = new WithdrawOrder();
        record.setId(withdrawOrderId);
        record.setStatus(statusEnum.getCode());
        record.setUpdateManager(updateUserId);
        record.setUpdateTime(now);

        return withdrawOrderMapper.updateByPrimaryKeySelective(record);
    }

    /**
     * 提现订单成功
     * @param withdrawOrderId
     * @param updateUserId
     * @param endTypeEnum
     * @return
     */
    public int successWithdrawOrder(Long withdrawOrderId, Long updateUserId, WithdrawEndTypeEnum endTypeEnum) {
        Date curDate = new Date();
        WithdrawOrder record = new WithdrawOrder();
        record.setId(withdrawOrderId);
        record.setStatus(WithdrawOrderStatusEnum.SUCCESS.getCode());
        record.setEndType(endTypeEnum.getValue());
        record.setSuccessTime(curDate);
        record.setEndTime(curDate);
        record.setUpdateTime(curDate);
        record.setUpdateManager(updateUserId);

        return withdrawOrderMapper.updateByPrimaryKeySelective(record);
    }

    /**
     * 提现订单失败
     * @param withdrawOrderId
     * @param updateUserId
     * @param endTypeEnum
     * @return
     */
    public int failWithdrawOrder(Long withdrawOrderId, Long updateUserId, WithdrawEndTypeEnum endTypeEnum) {
        Date curDate = new Date();
        WithdrawOrder record = new WithdrawOrder();
        record.setId(withdrawOrderId);
        record.setStatus(WithdrawOrderStatusEnum.FAIL.getCode());
        record.setEndType(endTypeEnum.getValue());
        record.setEndTime(curDate);
        record.setUpdateTime(curDate);
        record.setUpdateManager(updateUserId);

        return withdrawOrderMapper.updateByPrimaryKeySelective(record);
    }

    /**
     * 查询当前提现应垫付总额
     *
     * @return
     */
    public BigDecimal queryAdvanceAmountTotal() {
        initAdvanceAmountTotal();
        String advanceAmountTotalStr = redisTemplate.opsForValue().get(REDIS_KEY_ADVANCE_TOTAL);
        return new BigDecimal(advanceAmountTotalStr);
    }

    /**
     * 增加提现应垫付总额
     *
     * @param withdrawAmount
     */
    public void increAdvanceAmountTotal(BigDecimal withdrawAmount) {
        try{
            if(withdrawAmount == null){
                return;
            }
            if (withdrawAmount.compareTo(BigDecimal.ZERO) > 0) {
                initAdvanceAmountTotal();
                redisTemplate.opsForValue().increment(REDIS_KEY_ADVANCE_TOTAL, withdrawAmount.doubleValue());
            }
        }catch(Exception e){
            log.error("increAdvanceAmountTotal exception",e);
        }

    }

    /**
     * 减少提现应垫付总额
     *
     * @param withdrawAmount
     */
    public void decreAdvanceAmountTotal(BigDecimal withdrawAmount) {
        try{
            if(withdrawAmount == null){
                return;
            }
            if (withdrawAmount.compareTo(BigDecimal.ZERO) > 0) {
                initAdvanceAmountTotal();
                BigDecimal delta = BigDecimal.ZERO.subtract(withdrawAmount);
                redisTemplate.opsForValue().increment(REDIS_KEY_ADVANCE_TOTAL, delta.doubleValue());
            }
        }catch(Exception e){
            log.error("decreAdvanceAmountTotal exception",e);
        }

    }

    /**
     * 分页查询提现订单
     *
     * @param queryRequest
     * @return
     */
    public PageInfo<WithdrawOrder> pageWithdrawOrders(WithdrawOrderQueryRequest queryRequest) {
        WithdrawOrderExample example = new WithdrawOrderExample();
        example.setOrderByClause("create_time desc");
        WithdrawOrderExample.Criteria criteria = example.createCriteria();

        if (Objects.nonNull(queryRequest.getUserId())) {
            criteria.andUserIdEqualTo(queryRequest.getUserId());
        }
        if (!CollectionUtils.isEmpty(queryRequest.getAccountIds())) {
            criteria.andAccountIdIn(queryRequest.getAccountIds());
        }
        if (Objects.nonNull(queryRequest.getStatusEnum())) {
            criteria.andStatusEqualTo(queryRequest.getStatusEnum().getCode());
        }
        if (!CollectionUtils.isEmpty(queryRequest.getStatusEnumList())) {
            List<Byte> statusList = queryRequest.getStatusEnumList().stream().map(e -> e.getCode()).collect(Collectors.toList());
            criteria.andStatusIn(statusList);
        }
        if (Objects.nonNull(queryRequest.getMinWithdrawTime())) {
            criteria.andCreateTimeGreaterThanOrEqualTo(queryRequest.getMinWithdrawTime());
        }
        if (Objects.nonNull(queryRequest.getMaxWithdrawTime())) {
            criteria.andCreateTimeLessThanOrEqualTo(queryRequest.getMaxWithdrawTime());
        }

        //分页
        PageHelper.startPage(queryRequest.getPageNumber(), queryRequest.getPageSize(), queryRequest.isNeedCount());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        PageInfo<WithdrawOrder> pageInfo = new PageInfo(withdrawOrderList);
        return pageInfo;
    }

    /**
     * 查询提现订单列表
     *
     * @param queryRequest
     * @return
     */
    public List<WithdrawOrder> listWithdrawOrders(WithdrawOrder queryRequest) {
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();

        //如果需要其它参数请自行增加。
        if (Objects.nonNull(queryRequest.getUserId())) {
            criteria.andUserIdEqualTo(queryRequest.getUserId());
        }
        if (Objects.nonNull(queryRequest.getAccountId())) {
            criteria.andAccountIdEqualTo(queryRequest.getAccountId());
        }
        if (Objects.nonNull(queryRequest.getCreateTime())) {
            criteria.andCreateTimeLessThan(queryRequest.getCreateTime());
        }
        if (Objects.nonNull(queryRequest.getStatus())) {
            criteria.andStatusEqualTo(queryRequest.getStatus());
        }
        if (Objects.nonNull(queryRequest.getAuditStatus())) {
            criteria.andAuditStatusEqualTo(queryRequest.getAuditStatus());
        }
        example.setOrderByClause("id desc");
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }


    /**
     * 查询预提交数据，不包括最近5分钟内的
     *
     * @return
     */
    public List<WithdrawOrder> listWithdrawOrdersForPreSubmissionJob() {
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(WithdrawOrderStatusEnum.PRE_SUBMISSION.getCode());
        criteria.andCreateTimeLessThan(new DateTime(new Date()).minusMinutes(5).toDate());
        criteria.andUpdateTimeGreaterThan(new DateTime(new Date()).minusDays(2).toDate());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }

    /**
     * 查询所有T0出款中的提现记录
     *
     * @return
     */
    public List<WithdrawOrder> listT0WithdrawOrdersForSuccessFailureJob() {
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(WithdrawOrderStatusEnum.OUTING.getCode());
        criteria.andWithdrawTypeEqualTo(WithdrawTypeEnum.URGENT.getValue());
        criteria.andUpdateTimeGreaterThan(new DateTime(new Date()).minusDays(2).toDate());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }

    /**
     * 查询所有T1出款中的提现记录
     *
     * @return
     */
    public List<WithdrawOrder> listT1WithdrawOrdersForSuccessFailureJob() {
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andStatusEqualTo(WithdrawOrderStatusEnum.OUTING.getCode());
        criteria.andWithdrawTypeEqualTo(WithdrawTypeEnum.NORMAL.getValue());
        Date today = new DateTime().hourOfDay().withMinimumValue()
                .minuteOfHour().withMinimumValue()
                .secondOfMinute().withMinimumValue()
                .millisOfSecond().withMinimumValue().toDate();
        criteria.andUpdateTimeLessThan(today);
        criteria.andUpdateTimeGreaterThan(new DateTime().minusDays(3).toDate());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }

    /**
     * 查询所有审核通过/或不通过，但提现状态还是待确认的订单，不包括最近5分钟
     *
     * @return
     */
    public List<WithdrawOrder> listWithdrawOrdersForConfirmCancelJob() {
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andAuditStatusIn(Arrays.asList(WithdrawOrderAuditStatusEnum.PASS.getValue(), WithdrawOrderAuditStatusEnum.NOT_PASS.getValue()));
        criteria.andStatusEqualTo(WithdrawOrderStatusEnum.WAIT_CONFIRM.getCode());
        criteria.andUpdateTimeLessThan(new DateTime(new Date()).minusMinutes(5).toDate());
        criteria.andUpdateTimeGreaterThan(new DateTime(new Date()).minusDays(2).toDate());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }


    //初始化Redis中应垫付总额的值
    private void initAdvanceAmountTotal() {
        if(redisTemplate.hasKey(REDIS_KEY_ADVANCE_TOTAL)){
            return;
        }
        //从提现订单表中统计应垫付总额(所有提现状态为待确认,且提现方式为智能T0的订单需要垫资的金额)
        BigDecimal advanceAmountTotal = withdrawOrderMapperExt.queryAdvanceAmountTotal(null);
        if (Objects.isNull(advanceAmountTotal)) {
            advanceAmountTotal = BigDecimal.ZERO;
        }
        //初始化Redis中的值,有效期一小时
        redisTemplate.opsForValue().set(REDIS_KEY_ADVANCE_TOTAL, advanceAmountTotal.toString(), 1L, TimeUnit.HOURS);
    }

    public List<WithdrawOrder> listT0WithdrawOrdersByAccountId(Long accountId){
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andAccountIdEqualTo(accountId);
        criteria.andWithdrawTypeEqualTo(WithdrawTypeEnum.URGENT.getValue());
        criteria.andStatusEqualTo(WithdrawOrderStatusEnum.WAIT_CONFIRM.getCode());
        List<WithdrawOrder> withdrawOrderList = withdrawOrderMapper.selectByExample(example);
        return withdrawOrderList;
    }

    /**
     * 查询最近一次提现记录
     * @param userId
     * @return
     */
    public WithdrawOrder queryLastWithdrawSuccessRecord(String userId){
        return withdrawOrderMapperExt.selectLastWithdrawSuccessRecord(userId);
    }

	public WithdrawOrderResponseDTO getWithdrawOrderById(Long withdrawOrderId) {
		WithdrawOrder withdrawOrder=withdrawOrderMapper.selectByPrimaryKey(withdrawOrderId);
		if(withdrawOrder==null){
			return null;
		}
		WithdrawOrderResponseDTO dto=new WithdrawOrderResponseDTO();
		dto.setId(withdrawOrder.getId());
		dto.setAccountRole(withdrawOrder.getAccountRole());
		dto.setAmount(withdrawOrder.getAmount());
		dto.setRealAmount(withdrawOrder.getRealAmout());
		dto.setStatus(withdrawOrder.getStatus());
		dto.setCreateTime(withdrawOrder.getCreateTime());
		dto.setSuccessTime(withdrawOrder.getSuccessTime());
		return dto;
	}

    /**
     * 查询提现中的记录
     * @param accountId
     * @return
     */
    public List<WithdrawOrder> queryWithdrawingOrderList(Long accountId){
        WithdrawOrderExample example = new WithdrawOrderExample();
        WithdrawOrderExample.Criteria criteria = example.createCriteria();
        criteria.andAccountIdEqualTo(accountId);
        List<Byte> statusList = new ArrayList<Byte>();
        statusList.add(WithdrawOrderStatusEnum.PRE_SUBMISSION.getCode());
        statusList.add(WithdrawOrderStatusEnum.WAIT_CONFIRM.getCode());
        statusList.add(WithdrawOrderStatusEnum.OUTING.getCode());
        criteria.andStatusIn(statusList);

        return withdrawOrderMapper.selectByExample(example);
    }

    public BankInfoRespDTO getWithdrawingOrderBankInfo(Long bankCardId){
        BankInfoRespDTO bankInfoRespDTO = new BankInfoRespDTO();
        // 根据订单中的bankCardId查询银行卡信息
        BankCard bankCard = bankCardMapperExt.selectByPrimaryKey(bankCardId);
        // 查询银行名称+银行卡号
        BankType bankType = bankTypeMapperExt.selectBankTypeByCode(bankCard.getBankCode());

        bankInfoRespDTO.setBankNo(privacyMasks.decryptPrivacy(bankCard.getBankAccountNo()));
        bankInfoRespDTO.setBankName(bankType.getBankName());

        return bankInfoRespDTO;
    }
}
